package client;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.UnknownHostException;

import parameters.RLM_Parameters;


/**
 * Klasa koja predstavlja mehanizam za viseznacno (multicast) upucivanje poruke za otkrivanje servera, tj.
 * serverske adrese i porta. Poruke se salju na odgovarajucu grupu (multicast adresu) u kojoj se nalazi
 * RLM server. Kada server primi ovakvu poruku, on odgovara svojom IP adresom i brojem porta.
 * 
 * Scenario:
 * 1. Client salje GET_SERVER_INFO ip_address:port
 * Ovde su 'ip_address' i 'port' adresa i port servera na klijentu. Na ovu adresu i port, RLM server ce slati
 * poruku sa odgovorom o svojoj IP adresi i portu.
 * 2. Kada RLM server primi ovakvu poruku, on iz nje izvlaci adresu i port, povezuje se na tu adresu i port
 * direktnom TCP konekcijom i salje poruku tipa: SET_SERVER_INFO ip_address:port.
 * Ovde su 'ip_address' i 'port' adresa i port RLM servera koji ceka zahteve klijenata.
 * 
 * Za procesiranje primljene poruke sa RLM servera zaduzene su sledece klase:
 * ClientServer, SET_SERVER_INFO, CommandExecutor i ClientApplication.createSocketConnection.
 * 
 * @author UrosKrkic & Radivoje Popovic
 */
public class ClientMulticast extends Thread {

	/**
	 * Referenca na objekat aplikacije.
	 */
	private ClientApplication theApp;

	
	/**
	 * Poruka koja se salje kao multicast poruka.
	 */
	private String message = "";

	
	/**
	 * IP adresa koja predstavlja multicast grupu kojoj pripada RLM server.
	 */
	private InetAddress multicastGroup = null;

	
	/**
	 * Port koji predstavlja multicast port.
	 */
	private int multicastPort = 0;

	
	/**
	 * Multicast socket.
	 */
	private MulticastSocket multicastSocket = null;

	
	/**
	 * Network interface kojim se salju multicast poruke.
	 */
	private NetworkInterface networkInterface = null;
	
	
	/**
	 * Brojac poslatih poruka. Salje se najvise 'counter' poruka.
	 * Prekinuce izvrsavanje petlje ukoliko ne stigne odgovor ni na jednu poruku.
	 */
	private int counter = 10;

	
	/**
	 * Indikator izvrsavanje glavne petlje u run() metodi.
	 * Prekinuce izvrsavanje petlje, kao odgovor na multicast poruku. Setuje je se false u
	 * interrupt() metodi.
	 */
	private boolean loop = true;


	
	/**
	 * Konstruktor.
	 * @param theApp - referenca na objekat aplikacije
	 * @author UrosKrkic & Radivoje Popovic
	 */
	public ClientMulticast(ClientApplication theApp) {
		this.theApp = theApp;
		
		StringBuffer messageBuffer = new StringBuffer("GET_SERVER_INFO ");
		try {
			
			String hostName = InetAddress.getLocalHost().getHostName();
			
			InetAddress hostAddress = null;
			
			for (int i = 0; i < InetAddress.getAllByName(hostName).length; i++) {
				
				hostAddress = InetAddress.getAllByName(hostName)[i];
			
				if (hostAddress.isLoopbackAddress())
					continue;
				
				if ( (InetAddress.getAllByName(hostName)[i].getHostAddress().startsWith("192.168."))  ) {
					hostAddress = InetAddress.getAllByName(hostName)[i];
					break;
				}
			}
			
			String address = hostAddress.getHostAddress();
			
			if ( RLM_Parameters.getNetworkInterfaceToConnect() != null ) {
				address = RLM_Parameters.getNetworkInterfaceToConnect();
			}
			
			messageBuffer.append(address);
//			messageBuffer.append(InetAddress.getLocalHost().getHostAddress());
			multicastGroup = theApp.getMulticastIPAddress();
			multicastPort = theApp.getMulticastPort();
			
			if ( !multicastGroup.isMulticastAddress() ) {
				this.interrupt();
				return;
			}
			
			multicastSocket = new MulticastSocket(multicastPort);
			
			networkInterface = NetworkInterface.getByInetAddress(hostAddress);
//			networkInterface = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
			
			if ( networkInterface.isLoopback() == false && networkInterface.isUp() ) {
				multicastSocket.setNetworkInterface(networkInterface);
			}
			
			multicastSocket.setLoopbackMode(false);
		}
		catch (UnknownHostException e) {
			messageBuffer.append("0.0.0.0");
		}
		catch (IOException ioe) {
			ioe.printStackTrace();
		}
		
		messageBuffer.append(":");
//		messageBuffer.append(this.theApp.getClientSideServerPort());
		messageBuffer.append(RLM_Parameters.getTcpPort());
		
		this.message = messageBuffer.toString();
		
		this.start();
	}
	
	
	@Override
	public void run() {
		
		while ( counter > 0 && loop ) {
			try {
				// paket koji se salje kao multicast poruka
				DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), multicastGroup, multicastPort);
				
				multicastSocket.send(packet);
				
				// TODO obrisati
				System.out.println("===========================================================================");
				String sent = new String(packet.getData(), 0, packet.getLength());
				System.out.println("Sent: " + sent + " to: " + packet.getAddress()+":" + packet.getPort()) ;
				
				counter--;
				
				// nakon 2 sec. vrsi se ponovno slanje
				Thread.sleep(2000);
			}
			catch (IOException ioe) {
				ioe.printStackTrace();
			}
			catch (InterruptedException ie) {
//				ie.printStackTrace();
			}
		}
		
		// kada se izadje iz petlje, stopira se multicast mehanizam, tj. ovaj thread
		theApp.stopMulticasting();
	}
	
	
	@Override
	public void interrupt() {
		super.interrupt();
		
		loop = false;
	}
	
}
